#include "NamespaceContentBuilder.h"

#include "ScriptCompiler/AST/NamespaceNode.h"

#include "ScriptCompiler/ASTBuilder/AccessLevelBuilder.h"
#include "ScriptCompiler/ASTBuilder/ClassBuilder.h"
#include "ScriptCompiler/ASTBuilder/DelegateBuilder.h"
#include "ScriptCompiler/ASTBuilder/UsingBuilder.h"
#include "ScriptCompiler/ASTBuilder/EnumBuilder.h"
#include "ScriptCompiler/ASTBuilder/NamespaceBuilder.h"
#include "ScriptCompiler/ScriptFileContext.h"


CNamespaceContentBuilder::CNamespaceContentBuilder(std::shared_ptr<CScriptFileContext> InContext, std::shared_ptr<CNamespaceNode> InNamespace)
{
    Context = InContext;
    Namespace = InNamespace;
}

bool CNamespaceContentBuilder::ReadCurentToken()
{
    auto Token = Context->GetCurrentToken();

    switch(Token->Type)
    {
        case EScriptTokenType::Using:
        {
            auto NewUsingBuilder = std::make_shared<CUsingBuilder>(Context);
            Namespace->Usings.Add(NewUsingBuilder->Result);
            if(!NewUsingBuilder->ReadCurentToken())
            {
                return false;
            }
            return ReadCurentToken();
        }

        case EScriptTokenType::Namespace:
        {
            if(NextAccessLevel)
            {
                Context->RaiseError(10071,Token, U"Access level token must be before namespace");
                return false;
            }

            auto NewNamespaceBuilder = std::make_shared<CNamespaceBuilder>(Context);
            Namespace->Namespaces.Add(NewNamespaceBuilder->Result);
            return NewNamespaceBuilder->ReadCurentToken();

        }

        case EScriptTokenType::Public:
        case EScriptTokenType::Private:
        case EScriptTokenType::Protected:
        case EScriptTokenType::Internal:
        {
            auto NewAccessLevelBuilder = std::make_shared<CAccessLevelBuilder>(Context);
            NextAccessLevel= NewAccessLevelBuilder->Result;
            if(!NewAccessLevelBuilder->ReadCurentToken())
            {
                return false;
            }
            return ReadCurentToken();
        }

        case EScriptTokenType::Delegate:
        {
            auto NewDelegateBuilder = std::make_shared<CDelegateBuilder>(Context);
            if(NextAccessLevel)
            {
                NewDelegateBuilder->Result->AccessLevel = NextAccessLevel;
                NextAccessLevel = nullptr;
            }
            Namespace->Delegates.Add(NewDelegateBuilder->Result);

            if(!NewDelegateBuilder->ReadCurentToken())
            {
                return false;
            }
            return ReadCurentToken();
        }

        case EScriptTokenType::Struct:
        case EScriptTokenType::Interface:
        case EScriptTokenType::Class:
        {
            auto NewClassBuilder = std::make_shared<CClassBuilder>(Context);
            if(NextAccessLevel)
            {
                NewClassBuilder->Result->AccessLevel = NextAccessLevel;
                NextAccessLevel = nullptr;
            }
            Namespace->Classes.Add(NewClassBuilder->Result);
            if(!NewClassBuilder->ReadCurentToken())
            {
                return false;
            }

            return ReadCurentToken();
        }

        case EScriptTokenType::Enum:
        {
            auto NewEnumBuilder = std::make_shared<CEnumBuilder>(Context);
            if(NextAccessLevel)
            {
                NewEnumBuilder->Result->AccessLevel = NextAccessLevel;
                NextAccessLevel = nullptr;
            }
            Namespace->Enums.Add(NewEnumBuilder->Result);

            if(!NewEnumBuilder->ReadCurentToken())
            {
                return false;
            }

            return ReadCurentToken();
        }

        case EScriptTokenType::EndOfFile:
        case EScriptTokenType::RightBrace:
        {
            //meet end of namespace
            return true;
        }

        case EScriptTokenType::Semicolon:
        {
            //skip extra semicolon , may exist after a class right brace
            Context->CurrentTokenIndex++;
            return ReadCurentToken();
        }

        default:
        {
            Context->RaiseError(10073,Token, U"Unexpected token in namespace");
            return false;
        }
    }

    return true;
}
